#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
    // 等同于 char str[] = "hello";
    printf("%s\n", str);

    // 等同于
    const char *str2 = "hello";
    printf("%s\n", str2);

    // 长字符串写法
    // 1、这种写法的缺点是前面的空格也会被视作字符串内容；
    char str3[200] = "Hello, \
                    how are you \
                    today!";
    printf("%s\n", str3);

    // 2、这种长字符串的写法就很舒服；
    char str4[50] = "Hello, "
                    "how are you "
                    "today!";
    const char *str5 = "Hello, "
                       "how are you "
                       "today!";
    printf("%s\n", str4);
    printf("%s\n", str5);

    // 字符指针和字符数组，这两种声明字符串变量的写法基本是等价的，但是有两个差异。
    // 第一个差异是，指针指向的字符串，在 C 语言内部被当作常量，不能修改字符串本身

    // 为什么字符串声明为指针时不能修改，声明为数组时就可以修改？原因是系统会将字符串的字面量保存在内存的常量区，这个区是不允许用户修改的。
    // 但是，声明为数组时，编译器会给数组单独分配一段内存，字符串字面量会被编译器解释成字符数组，逐个字符写入这段新分配的内存之中，而这段新内存是允许修改的。
    // 为了提醒用户，字符串声明为指针后不得修改，可以在声明时使用const说明符，保证该字符串是只读的。
    // const char *str5 = "Hello, world!";

    // str5[0] = 'z'; // 不可预料的错误
    str4[0] = 'z'; // OK
    // 第二个差异是，指针变量可以指向其它字符串。
    // char* s = "hello";
    // s = "world";

    // size_t strlen(const char* s);
    // strlen()函数返回字符串的字节长度，不包括末尾的空字符\0;
    size_t length = strlen(str);
    printf("length: %zd\tsize: %zd\n", length, sizeof(str));

    // strcpy(char dest[], const char source[])
    // C 语言提供了strcpy()函数，用于将一个字符串的内容复制到另一个字符串，相当于字符串赋值
    char str6[] = "hello";
    char str7[100];
    strcpy(str7, str6); // strcpy的返回值是一个字符串指针（即char*），指向第一个参数

    str7[0] = 'z';
    printf("%s\n", str6);
    printf("%s\n", str7);

    // char *strncpy(char *dest, char *src, size_t n);
    // strncpy()跟strcpy()的用法完全一样，只是多了第3个参数，用来指定复制的最大字符数，防止溢出目标字符串变量的边界
    // strncpy(str1, str2, sizeof(str1) - 1);
    // str1[sizeof(str1) - 1] = '\0';
    char str8[20];
    strncpy(str8, str2, sizeof(str8) - 1);
    str8[sizeof(str8) - 1] = '\0'; // 手动添加最后一位结束标志
    printf("%s\n", str8);
    return 0;
}